home *** CD-ROM | disk | FTP | other *** search
- /* UpDateCopy.e
- ** Copies multiple files/directories to one destination.
- ** Not existing directories are created.
- ** Already existing files are only replaced by newer ones
- ** (UpDateCopy first checks for versions-string and then
- ** for dates).
- **
- ** $VER: UpDateCopy.e 1.5 (17.03.98)
- **
- ** This program is Cardware. If you use it you should send an Email to
- ** the author. Also small presents are very welcome.
- ** You may use this sourcefile or parts of it freely in your programs.
- ** But please do not spread a modified version under this name (UpDateCopy).
- ** For Bugreports, ideas or anything else send a Email to:
- ** ss37@irz.inf.tu-dresden.de
- **
- ** Sven Steiniger, 1996-98
- **
- ** Fold-start: ->// ""
- ** Fold-stop: ->\\
- **
- ** History:
- ** 1.5 : - anchor structure was not longword aligned
- ** : - added lha-compatible date compare mode
- ** 1.4 : - FIXED: directories may be created, even in testmode
- ** : - FIXED: protection bits may be modified in testmode
- ** 1.3 : - the file-io buffer is now allocated once at start
- ** . - cleaned up
- ** 1.2 : - fixed small bug in getVersion()
- ** 1.1 : - uses now asyncio.library for fantastic speed
- ** 1.00: - implemented SAB, CAB options
- ** 0.54: - implemented NOASK-option
- ** 0.53: - now user is asked on errors. Doesn't works perfect but I don't now
- ** how to implement it properly without adding to much extra code.
- ** 0.52: - improved SMARTINFO-option
- ** 0.51: - removed bug of version-scan-routines
- ** 0.50: - added FAST-option
- ** 0.49: - added SMARTINFO option
- ** - added wildcard-check. Now you can also use '*' as wildcard and
- ** you can also pass devices as sources (eq. ram:)
- ** 0.48: - used exthelp-feature OF ReadArgs()
- ** - no longer needs 'c:copy'-command. Implemented an own routine.
- ** 0.47: - file was not closed before SetProtection()/SetFileDate()
- ** 0.46: - added PAPF-option -> archive protection flag is not changed
- ** 0.45: - added CLONE-option -> Datestamp is copied
- ** 0.44: - removed a unnecessary module. Saves 1K of executable size!
- ** - removed unnecessary ExamineFH()
- ** - now file gets only examined if really necessary
- ** - optimized a bit
- ** - FIXED: TESTMODE was buggy -> Directories were still created.
- ** 0.43: - add new option TESTMODE; dont copy/replace files
- ** 0.42: - add new option NODATECHECK; dont compare datestamps
- ** 0.41: - replaced all reads/writes by fread/fwrite
- ** 0.40: - first public release
- */
-
- OPT OSVERSION=37
- OPT REG=5
-
- MODULE 'dos/dos','dos/dosasl','dos/dostags','dos/rdargs','dos/stdio',
- 'asyncio','libraries/asyncio'
- MODULE 'sven/anchorPath',
- 'sven/eAddPart'
-
- RAISE "MEM" IF String()=NIL,
- "^C" IF CtrlC()=TRUE,
- "copy" IF FileLength()=-1,
- "MEM" IF AllocDosObject()=NIL,
- "fatt" IF SetProtection()=DOSFALSE,
- "fatt" IF SetComment()=DOSFALSE,
- "fatt" IF SetFileDate()=DOSFALSE
-
-
- CONST MAXPATH=256, -> Maximum path length
- SPACEADD=3, -> Number of spaces per indent
- SAFETYBYTES=100, -> Maximum length of versionstring
- BIGFILEMEM=16384 -> Files bigger than that are not read
- -> completly into memory
-
- CONST PATHLENGTH=MAXPATH-1,
- BIGFILESIZE=BIGFILEMEM-SAFETYBYTES
-
- CONST ASYNCBUFFER=BIGFILESIZE*2
-
-
- ENUM PDIR_Error,
- PDIR_Skipped,
- PDIR_Created
-
- ENUM PFILE_Error,
- PFILE_Skipped,
- PFILE_Copied,
- PFILE_Replaced
-
- ENUM ASKUSER_Abort,
- ASKUSER_Retry,
- ASKUSER_Skip
-
- DEF frompath[MAXPATH]:STRING, -> actual sourcepath
- fromlist:PTR TO LONG, -> pointer to array of sourcestrings
- topath[MAXPATH]:STRING, -> destinationpath
- doinfo, -> should we write informations ?
- recursiv, -> scan recursively through subdirectories ?
- ignoreprotection, -> clear delete-protection ?
- checkversion, -> compare version-strings ?
- checkdates, -> compare datestamps ?
- testmode, -> test modus ? (do not copy/replace files)
- clone, -> copy datestamp ?
- cleararchivebit, -> clear archivebit OF copied file
- smartinfo, -> only display copied/replaced files
- fastdisplay, -> no linefeed
- interaction, -> ask user on errors
- setarchivebit, -> set Archive bit
- checkarchivebit, -> check Archive bit
- lousydatestamp, -> lha compatible datestamp compare
- dirlock=NIL -> PTR TO lock-structure OF destination path
-
- DEF buffer=NIL:PTR TO CHAR -> the global buffer used for all file-io.
- -> Contains BIGFILEMEM+8 Bytes
-
- PROC main() HANDLE
- ->// "main()"
- DEF rdargs=NIL,
- myargs:PTR TO LONG,
- template,
- myrdargs=NIL:PTR TO rdargs,
- exthelp[1000]:STRING
-
- asynciobase:=OpenLibrary('asyncio.library',39)
- IF asynciobase=NIL THEN Raise("asio")
-
- /* Initialize argument-array */
- myargs:=[
- NIL, -> fromlist
- NIL, -> destination path
- FALSE, -> ~showinfo
- FALSE, -> scan recursiv throuhg subdirectories
- FALSE, -> ignore protectionbits
- FALSE, -> deep scan
- FALSE, -> ~compare datestamps
- FALSE, -> testmode
- FALSE, -> clone
- FALSE, -> preserve archive protection flag
- FALSE, -> smartinfo
- FALSE, -> fastdisplay
- FALSE, -> interaction
- FALSE, -> set Archivebit
- FALSE, -> check Archivbit
- FALSE -> precise datestamp
- ]:LONG
-
- myrdargs:=AllocDosObject(DOS_RDARGS,NIL)
-
- StrCopy(exthelp, '\n'+
- ' UpDateCopy v1.5\n'+
- ' -----------------\n'+
- ' Copy files and directories.\n'+
- ' Not existing directories are created and\n'+
- ' already existing files are only replaced\n'+
- ' by newer ones. Allows pattern-matching and\n'+
- ' multiple sourcedirectories/files.\n\n'+
- 'Sven Steiniger, 1996-98\n'
- )
- StrAdd(exthelp, '\n'+
- ' FROM - source directory/file(s)\n'+
- ' TO - destination directory\n'+
- ' QUIET - no outputs\n'+
- ' ALL - scans through subdirectories recursively\n'+
- ' FORCE - ignore delete-protectionbit\n'+
- ' DEEP - compare version-strings\n'+
- ' NODATECHECK - don''t compare datestamps\n'+
- ' TESTMODE - neither copy/replace files nor create directories\n'+
- ' CLONE - copy datestamp\n'+
- ' PAB - don''t clear the archive protection flag\n'+
- ' SMARTINFO - only displays copied/replaced files\n'+
- ' FAST - fast output. No linefeeds\n'+
- ' NOASK - don''t ask user on errors\n'+
- ' SETARCBIT - set archive-bit on copied directories/files\n'+
- ' CHECKARCBIT - only copy files with no archive-bit\n'+
- ' PRECISEDS - compare datestamps precisly\n'
- )
- myrdargs.exthelp:=exthelp
-
- /* Parse Commandline */
- IF rdargs:=ReadArgs(template:='FROM/M,TO/A,QUIET/S,ALL/S,FORCE/S,DEEP/S,'+
- 'NDC=NODATECHECK/S,TESTMODE/S,CLONE/S,PAB=PAPF/S,'+
- 'SMARTINFO/S,FAST/S,NOASK/S,SAB=SETARCBIT/S,'+
- 'CAB=CHECKARCBIT/S,PRECISEDS/S',
- myargs,myrdargs)
-
- /* Copy Datas to global variables */
- fromlist := myargs[0]
- StrCopy(topath,myargs[1])
- doinfo := Not(myargs[2])
- recursiv := myargs[3]
- ignoreprotection := myargs[4]
- checkversion := myargs[5]
- checkdates := Not(myargs[6])
- testmode := myargs[7]
- clone := myargs[8]
- cleararchivebit := Not(myargs[9])
- smartinfo := myargs[10]
- fastdisplay := myargs[11]
- interaction := Not(myargs[12])
- setarchivebit := myargs[13]
- checkarchivebit := myargs[14]
- lousydatestamp := Not(myargs[15])
-
- IF fromlist=NIL THEN Throw("ARGS",'No source specified.')
- init_arguments()
-
- IF doinfo
- PrintF('Updating files to "\s".\n',topath)
- IF testmode THEN PrintF('** Testmode **\n')
- ENDIF
-
- IF fastdisplay
- PrintF('\n')
- smartinfo:=FALSE
- ENDIF
-
- buffer:=NewR(BIGFILEMEM+8) -> Allocate file-io buffer
-
- WHILE fromlist[] -> 'fromlist' is array of string-pointers
- StrCopy(frompath,fromlist[]++) -> copy the string and increment 'fromlist'
- checkWildCard(frompath)
- scan_directory(frompath,'',1) -> then process this directory
- ENDWHILE
-
- ENDIF
-
- EXCEPT DO -> Cleanup
-
- IF buffer THEN Dispose(buffer)
-
- IF fastdisplay THEN reportLine(0,'',NIL,NIL)
-
- free_arguments()
- IF rdargs THEN FreeArgs(rdargs)
- IF myrdargs THEN FreeDosObject(DOS_RDARGS,myrdargs)
-
- CloseLibrary(asynciobase)
-
- IF exception
- /* Print error description */
- PrintF('Error: ')
- SELECT exception
- CASE "MEM" ; PrintF('Not enough memory.\n')
- CASE "addp" ; PrintF('Path too long !?\n')
- CASE "^C" ; PrintF('User abort.\n')
- CASE "copy" ; PrintF('Could not copy file.\n')
- CASE "open" ; PrintF('Could not open file.\n')
- CASE "anly" ; PrintF('Could not analyse file.\n')
- CASE "fatt" ; PrintF('Could not set file attributes.\n')
- CASE "asio" ; PrintF('Could not open asyncio.library v39.\n')
- DEFAULT ; PrintF('\s\n',exceptioninfo)
- ENDSELECT
- ELSE
- PrintF('Finished.\n')
- ENDIF
-
- CleanUp(exception)
-
- ENDPROC
- ->\\
-
- CHAR 0,'$VER: UpDateCopy 1.5 (17.03.98)',0
-
-
- /* checks the arguments provied by user
- */
- PROC init_arguments()
- ->// "init_arguments()"
-
- IF (dirlock:=Lock(topath,SHARED_LOCK))=NIL -> check if destination
- Throw("dir",'Could not lock destination directory') -> directory exists
- ENDIF
-
- /* Enable Version Check if datestamp-check is switch off */
- IF Not(checkdates) THEN checkversion:=TRUE
-
- ENDPROC
- ->\\
-
- /* handles so special cases with wildcards
- ** Parameter:
- ** file - file/dir to be checked
- */
- PROC checkWildCard(file:PTR TO CHAR)
- ->// "checkWildCard"
- DEF len
-
- len:=StrLen(file)
- IF file[len-1]="*"
- -> replace '*'-wildcard by '#?'
- CopyMem('#?',file+len-1,3)
- ELSEIF file[len-1]=":"
- -> it's an device. Therefore add wildcard
- CopyMem('#?',file+len,3)
- ENDIF
-
- ENDPROC
- ->\\
-
- /* Cleanup datas allocated during init_arguments
- */
- PROC free_arguments()
- ->// "free_arguments()"
- UnLock(dirlock) -> Unlock destination directory
- ENDPROC
- ->\\
-
- /* Scans a directory and copies files/creates subdirectories
- ** Parameter:
- ** directory - name of directory to be scanned
- ** path - the current delta path
- ** depth - recursion level
- ** Example:
- ** The source path is "esource:src".
- ** 'directory' is "esource:src/tools/file".
- ** Then 'path' have to be "tools/file".
- */
- PROC scan_directory(directory,path,depth) HANDLE
- ->// "scan_directory()"
- DEF info:PTR TO fileinfoblock,
- anchor=NIL:PTR TO anchorpath,
- error,
- fullpath,
- mypath[MAXPATH]:STRING,
- length
-
- /* Create and initialize anchor structure needed for
- ** scanning through directory
- */
- anchor,fullpath:=allocAnchorPath(PATHLENGTH)
-
- error:=MatchFirst(directory,anchor)
- WHILE error=DOSFALSE
-
- CtrlC()
-
- info:=anchor.info -> get fileinfoblock
- IF info.direntrytype>0 -> is it a directory ?
-
- eAddPartC(mypath, path, info.filename) -> init new delta path including new subdirectory
-
- process_directory(fullpath,info,mypath, -> knows what to do with this
- depth*SPACEADD) -> directory
-
- IF recursiv -> scan subdirectories ?
- length:=StrLen(fullpath)
- IF (length+5)<MAXPATH
- CopyMem('/#?',fullpath+length,4) -> add pattern matching
- ENDIF
- scan_directory(fullpath,mypath,depth+1) -> call our self with new subdirectory
- fullpath[length]:=0 -> remove pattern matching
- ENDIF
-
- ELSE
-
- process_file(fullpath,info,path, -> knows what to do with it
- depth*SPACEADD)
-
- ENDIF
-
- error:=MatchNext(anchor) -> Next entry
- ENDWHILE
-
- EXCEPT DO
-
- IF anchor
- MatchEnd(anchor) -> Clean up
- freeAnchorPath(anchor)
- ENDIF
-
- ReThrow()
-
- ENDPROC
- ->\\
-
- /* takes care of fastdisplay-option
- */
- PROC reportLine(spaceanz,fmtstr,arg1,arg2,doCR=TRUE)
- ->// "reportLine()"
-
- IF fastdisplay
- PrintF([141,$1b,"[","M",0]:CHAR)
- spaceanz:=SPACEADD
- ENDIF
- WHILE spaceanz-->=0 DO FputC(stdout," ")
- PrintF(fmtstr,arg1,arg2) -> Write directory name & status
- IF doCR THEN PrintF('\n')
-
- ENDPROC
- ->\\
-
- /* Ask the user what to on error.
- ** Returns ASKUSER_xxx
- */
- PROC askError()
- ->// "askError()"
- DEF c=0,
- ret=ASKUSER_Abort
-
- IF interaction
-
- PrintF(' (R)etry / (S)kip / (A)bort ? ')
- Flush(stdout)
-
- REPEAT
- UNTIL WaitForChar(stdin,9999999999)=DOSTRUE
- c:=FgetC(stdin)
- Flush(stdin)
-
- IF (c="r") OR (c="R")
- ret:=ASKUSER_Retry
- ELSEIF (c="a") OR (c="A")
- ret:=ASKUSER_Abort
- ELSE
- c:="s"
- ret:=ASKUSER_Skip
- ENDIF
- ENDIF
- ->IF c THEN PrintF('\c',c)
- IF c=0 THEN PrintF('\n')
- ENDPROC ret
- ->\\
-
- /* Prints a directory status.
- ** Parameter:
- ** spaces - number of leading spaces
- ** status - status (PDIR_Error, PDIR_Skipped, PDIR_Created)
- ** path - directory path
- */
- PROC printDirStatus(spaces,status,path)
- ->// "printDirStatus()"
- DEF stri
-
- SELECT status
- CASE PDIR_Created ; stri:='created'
- CASE PDIR_Skipped ; IF smartinfo THEN RETURN -> don't display skipped dirs with smartinfo
- stri:='skipped'
- DEFAULT ; stri:='Error!!'
- ENDSELECT
-
- -> Write directory name & status
- IF doinfo OR (status=PDIR_Error)
- reportLine(spaces,'\e[1m\s\e[0m..\s',path,stri,status<>PDIR_Error)
- ENDIF
-
- ENDPROC
- ->\\
-
- /* Process an directory. If it doesnt exists it will be created
- ** Parameter:
- ** directory - full path of source directory
- ** info - pointer to fileinfoblock of source directory
- ** path - delta path of directory
- */
- PROC process_directory(directory,info:PTR TO fileinfoblock,path,spaces)
- ->// "process_directory()"
- DEF stri[MAXPATH]:STRING,
- lock
-
- eAddPartC(stri,topath,path) -> thats the destion-directory
-
- IF checkarchivebit AND
- (info.protection AND FIBF_ARCHIVE) -> Archive-bit set ?
- printDirStatus(spaces, PDIR_Skipped, path)
-
- ELSEIF lock:=Lock(stri,SHARED_LOCK) -> Exists this Directory ?
- printDirStatus(spaces, PDIR_Skipped, path)
- UnLock(lock) -> Unlock directory
- IF setarchivebit THEN SetProtection(directory,info.protection OR FIBF_ARCHIVE)
-
- ELSEIF testmode -> Don't create directory in testmode
- printDirStatus(spaces, PDIR_Created, path) -> but write out a info
-
- ELSEIF lock:=needThisDir(stri) -> Create the directory
- printDirStatus(spaces, PDIR_Created, path)
- UnLock(lock) -> No erros, unlock directory
- copyAdditionalInformations(stri,info)
- IF setarchivebit THEN SetProtection(directory,info.protection OR FIBF_ARCHIVE)
-
- ELSE -> Directory could not be created
- printDirStatus(spaces, PDIR_Error, path) -> as it does not exists before
- lock:=askError()
- SELECT lock
- CASE ASKUSER_Abort
- Throw("dir",'Could not create directory')-> something went wrong
- CASE ASKUSER_Retry
- process_directory(directory,info,path,spaces)
- ENDSELECT
- ENDIF
-
- ENDPROC
- ->\\
-
- /*
- ** Truncates the tick-entry of the datestamp to be a multiple of 100.
- ** (lha does this!!)
- ** It seems they transform the ticks into seconds (/50) and made
- ** them a multiple of 2.
- */
- PROC simplifyDateStamp(ds:PTR TO datestamp)
- ->// "simplifyDateStamp"
- ds.tick:=ds.tick/100*100
- ENDPROC
- ->\\
-
- /*
- ** Gets the PathPart of an string.
- ** 'dst' must be an EString!
- */
- PROC getPath(dst, stri) IS StrCopy(dst, stri, PathPart(stri)-stri)
-
- /* Checks if an directory already exists.
- ** If not it (and all necessary parent directories) are created.
- ** Parameter:
- ** stri - full name of the directory to be created
- ** Returns a lock to the directory or NIL in case of an error.
- */
- PROC needThisDir(stri)
- ->// "needThisDir()"
- DEF lock, hstri[MAXPATH]:STRING
-
- ->WriteF('need dir >>\s<<\n',stri)
- CtrlC()
-
- IF lock:=Lock(stri, SHARED_LOCK)
- -> Already exists
- ELSEIF lock:=needThisDir(getPath(hstri, stri))
- -> All parent directories exists
- UnLock(lock)
- lock:=CreateDir(stri)
- ENDIF
-
- ENDPROC lock
- ->\\
-
- /* Prints a file status.
- ** Parameter:
- ** spaces - number of leading spaces
- ** status - status (PFILE_Error, PFILE_Skipped, PFILE_Created, PFILE_Skipped)
- ** file - filename
- */
- PROC printFileStatus(spaces,status,file)
- ->// "printFileStatus()"
- DEF stri
-
- SELECT status
- CASE PFILE_Copied ; stri:='copied'
- CASE PFILE_Replaced ; stri:='replaced'
- CASE PFILE_Skipped ; IF smartinfo THEN RETURN -> don't display skipped dirs with smartinfo
- stri:='skipped'
- DEFAULT ; stri:='Error!!'
- ENDSELECT
- -> Write file status
- IF doinfo OR (status=PFILE_Error)
- reportLine(spaces,'\s..\s',file,stri,status<>PFILE_Error)
- ENDIF
-
- ENDPROC
- ->\\
-
- /* Processes an file. If it does not exists or is newer it is copied.
- ** Parameter:
- ** file - full source filename (include path)
- ** info - ptr to fileinfoblock of sourcefile
- ** path - deltapath to directory (exclude filename !)
- */
- PROC process_file(file,info:PTR TO fileinfoblock,path,spaces) HANDLE
- ->// "process_file()"
- DEF stri[MAXPATH]:STRING,
- filepath[MAXPATH]:STRING,
- fh=NIL,
- toinfo=NIL:PTR TO fileinfoblock,
- result=0,
- frombuf=NIL,
- lock,
- hstri[MAXPATH]:STRING
-
- eAddPartC(filepath,path,info.filename) -> create deltapath inclusive filename
- eAddPartC(stri,topath,filepath) -> create full destination filepath
-
- IF checkarchivebit AND
- (info.protection AND FIBF_ARCHIVE) -> Archive-bit set
-
- -> Do not copy already archived files
- printFileStatus(spaces, PFILE_Skipped, filepath)
-
- ELSEIF fh:=Open(stri,MODE_OLDFILE) -> Open destinationfile
-
- IF checkversion THEN result,frombuf:=compareversion(file,stri)
-
- IF checkdates AND (result=0)
- /* Fileinfoblock have to be LONGWORD-aligned therefore
- ** use dos.library to create this
- */
- toinfo:=AllocDosObject(DOS_FIB,NIL)
-
- -> fill fileinfoblock
- IF ExamineFH(fh,toinfo)=DOSFALSE THEN Throw("file",'Could not examine file ?!')
-
- /* lha compatible datestamp compare ?
- */
- IF lousydatestamp
- simplifyDateStamp(info.datestamp)
- simplifyDateStamp(toinfo.datestamp)
- ENDIF
-
- /* Compare versionstrings was either not specified by user
- ** or was not successfull. Therefore Compare filedates if
- ** the user want it.
- */
- result:=CompareDates(toinfo.datestamp,info.datestamp)
- ENDIF
-
- IF result>0 -> fromfile newer than tofile ?
- Close(fh) ; fh:=NIL -> close destination
-
- IF Not(testmode)
- IF ignoreprotection THEN SetProtection(stri,0) -> Clear protectionflags if specified
- copyfile(file,info,stri,frombuf) -> copy the file
- ENDIF
-
- printFileStatus(spaces, PFILE_Replaced, filepath)
- ELSE
- printFileStatus(spaces, PFILE_Skipped, filepath)
- ENDIF
-
- ELSEIF testmode -> destination does not exists but testmode
- printFileStatus(spaces, PFILE_Copied, filepath)
-
- ELSEIF lock:=needThisDir(getPath(hstri, stri)) -> destination does not exists
- UnLock(lock)
- copyfile(file,info,stri) -> copy file
- printFileStatus(spaces, PFILE_Copied, filepath)
- ENDIF
-
- EXCEPT DO -> Cleanup
-
- IF toinfo THEN FreeDosObject(DOS_FIB,toinfo)
- IF fh THEN Close(fh)
-
- IF exception
- printFileStatus(spaces, PFILE_Error, filepath)
- result:=askError()
- SELECT result
- CASE ASKUSER_Abort
- ReThrow() -> something went wrong
- CASE ASKUSER_Retry
- process_file(file,info,path,spaces)
- ENDSELECT
- ENDIF
-
- ENDPROC
- ->\\
-
-
- /* Copies a file. For files >BIGFILESIZE Bytes c:copy is
- ** used.
- ** Parameter:
- ** fromfile - full path of sourcefile
- ** tofile - full path of destinationfile
- ** frombuf - Contents of sourcefile. If NIL then sourcefile
- ** is read
- */
- PROC copyfile(fromfile,frominfo:PTR TO fileinfoblock,tofile,frombuf=NIL) HANDLE
- ->// "copyfile()"
- DEF fhfrom=NIL,
- fhto=NIL,
- length,
- steplength,actlength
-
- IF ((fhfrom:=OpenAsync(fromfile,MODE_READ,ASYNCBUFFER))<>NIL) AND -> open sourcefile
- ((fhto:=OpenAsync(tofile,MODE_WRITE,ASYNCBUFFER))<>NIL) -> open destinationfile
-
- IF (length:=FileLength(fromfile))>BIGFILESIZE
- /* Files >BIGFILESIZE are not read completly into memory but in parts
- ** of BIGFILESIZE Bytes.
- */
-
- /* steplength is the length of the current block to be read */
- /* actlength is the position within the file */
- steplength:=actlength:=BIGFILESIZE
-
- REPEAT
- /* Read next block to buffer */
- IF ReadAsync(fhfrom,buffer,steplength)<>steplength THEN Raise("copy")
-
- /* Write buffer to destinationfile */
- IF WriteAsync(fhto,buffer,steplength)<>steplength THEN Raise("copy")
-
- IF (actlength:=actlength+BIGFILESIZE)>length
- steplength:=length-(actlength-BIGFILESIZE)
- ENDIF
-
- /* Read until version-string was found or EOF */
- UNTIL steplength<=0
-
- ELSE
-
- IF frombuf -> we already know the
- -> contents of the sourcefile
- /* Write buffer to destinationfile */
- IF WriteAsync(fhto,frombuf,length)<>length THEN Raise("copy")
- ELSE
-
- /* Read sourcefile into buffer */
- IF ReadAsync(fhfrom,buffer,length)<>length THEN Raise("copy")
- /* Write buffer TO destinationfile */
- IF WriteAsync(fhto,buffer,length)<>length THEN Raise("copy")
- ENDIF
-
- ENDIF
- ELSE -> There went something wrong
- Raise("copy")
- ENDIF
-
- EXCEPT DO
-
- IF fhfrom THEN CloseAsync(fhfrom)
- IF fhto THEN CloseAsync(fhto)
-
- ReThrow()
-
- /* Everything went ok. Copy comment, protection flags etc. */
- copyAdditionalInformations(tofile,frominfo)
- IF setarchivebit THEN SetProtection(fromfile,frominfo.protection OR FIBF_ARCHIVE)
-
- ENDPROC
- ->\\
-
- /* Copies extra informations like protectionbits, comment etc.
- ** 'name' must be a valid File- or Directory path
- ** 'info' is the fileinfoblock of the original file/directories
- ** (the dates are copied from it)
- **
- ** Note: the file may not be opened or locked!
- */
- PROC copyAdditionalInformations(name,info:PTR TO fileinfoblock)
- ->// "copyAdditionalInformations()"
-
- DEF protection
-
- /* Clear protectionbit (or not) */
- protection:=info.protection
- IF cleararchivebit THEN protection:=protection AND Not(FIBF_ARCHIVE)
-
- SetProtection(name,protection) -> Copy protection bits
- SetComment(name,info.comment) -> Copy comment
-
- /* Copy datestamp if clone option is activated */
- IF clone THEN SetFileDate(name,info.datestamp)
-
- ENDPROC
- ->\\
-
-
- /* Compares the version-strings of two files
- ** Parameter:
- ** fromfile - full path of sourcefile
- ** tofile - full path of destinationfile
- **
- ** Returns
- ** -1 if tofile-version>=fromfile-version
- ** 0 on error
- ** 1 if <
- ** *AND* the contents of the sourcefile or NIL
- */
- PROC compareversion(fromfile,tofile)
- ->// "compareversion()"
- DEF frombuf:PTR TO CHAR,
- version1,base1,
- version2,base2
-
- /* We dont need the destination file. Therefore don't get buffer.
- */
- version2,base2:=getVersionOfFile(tofile)
-
- /* if version is -1 then no version-string was found */
- IF version2=-1 THEN RETURN 0,NIL
-
- version1,base1,frombuf:=getVersionOfFile(fromfile)
- IF version1=-1 THEN RETURN 0,frombuf
-
- /* Compare the version, reversion of source/destionation
- ** multiply with other base to get same number of signifant numbers
- */
- RETURN IF (version2*base1)>=(version1*base2) THEN -1 ELSE 1,frombuf
-
- ENDPROC
- ->\\
-
-
- /* Gets the version-number of a file
- ** Parameter:
- ** filename - full path of file
- ** Returns the version, base and a buffer with the contents of the file
- ** (description for version,base see getversion())
- ** buffer may be NIL if file was to large
- */
- PROC getVersionOfFile(filename) HANDLE
- ->// "getVersionOfFile()"
- DEF fh,
- frombuf=NIL,
- version,base,
- steplength,filelength,actlength
-
- IF (fh:=OpenAsync(filename,MODE_READ,ASYNCBUFFER))=NIL THEN Raise("open")
-
- filelength:=FileLength(filename)
- IF filelength>BIGFILEMEM
- /* Files >BIGFILESIZE are not read completly into memory but in parts
- ** of BIGFILESIZE Bytes.
- ** As we may skip a versionstring the last SAFETYBYTES are copied
- ** to start of new BIGFILESIZE block everytime.
- */
-
- /* Read filecontents into buffer */
- IF ReadAsync(fh,buffer,BIGFILEMEM)<>BIGFILEMEM THEN Raise("anly")
-
- /* steplength is the length of the current block to be read */
- steplength:=BIGFILESIZE
- /* actlength is the position within the file */
- actlength:=BIGFILEMEM
-
- REPEAT
-
- /* Get version. If version=-1 then read next block */
- version,base:=getversion(buffer,steplength+SAFETYBYTES)
- IF version=-1
-
- /* The version-string was maybe at the end of buffer
- ** and therfore skipped. Copy the last SAFETYBYTES bytes
- ** to start of buffer to not lose the version-string.
- */
- CopyMem(buffer+steplength,buffer,SAFETYBYTES)
-
- /* Increase actlength. If actlength is greater than filelength
- ** then set steplength to the number of bytes left.
- */
- actlength:=actlength+BIGFILESIZE
- IF actlength>filelength
- steplength:=filelength-(actlength-BIGFILESIZE)
- ENDIF
-
- /* Read next block to buffer. If steplength<0 then EOF is reached */
- IF steplength>0
- IF ReadAsync(fh,buffer+SAFETYBYTES,steplength)<>steplength THEN Raise("anly")
- ENDIF
-
- ENDIF
-
- /* Read until version-string was found or EOF */
- UNTIL (version<>-1) OR (steplength<=0)
-
- ELSE
- /* We have got a small file. Load it completly into memory.
- */
- IF ReadAsync(fh,buffer,filelength)<>filelength THEN Raise("anly")
-
- /* Notify that we have the file already loaded
- */
- frombuf:=buffer
-
- /* get versions and reversion of sourcefile */
- version,base:=getversion(buffer,filelength)
- ENDIF
-
-
- EXCEPT DO
-
- /* Close the filehandle */
- IF fh THEN CloseAsync(fh)
- ReThrow()
-
- ENDPROC version,base,frombuf
- ->\\
-
-
- /* Search for a version-string in a file
- ** Parameter:
- ** buffer - Contents of file
- ** bufferlength - length of buffer in bytes
- ** Returns version,base
- ** if no version-string was found then -1 is returned as version
- ** Example: version=81259, base=10000 means
- ** Versionnumber is 81259/10000=8.1259
- */
- PROC getversion(buffer:PTR TO CHAR,bufferlength)
- ->// "getversion()"
- -> Works with up to 2Gig
- DEF version:REG,base:REG
-
- MOVEA.L buffer,A0 -> A0..buffer
- MOVE.L bufferlength,D0 -> D0..bufferlength
- MOVEQ #"$",D1
- MOVE.L #"VER:",D2
- gv_search_loop:
- SUBQ.L #1,D0
- BLT.S gv_error -> bufferend reached ?
- CMP.B (A0)+,D1
- BNE.S gv_search_loop -> Found a "$" ?
- CMP.L (A0),D2 -> Yes, then next characters="VER:"
- BNE.S gv_search_loop
-
- SUBQ.L #4,D0 -> We have found a version-string
- BLT.S gv_error
- ADDQ.L #4,A0 -> skip "VER:"
-
- MOVEQ #" ",D1
- gv_skipspaces1: -> skip all spaces before programname
- SUBQ.L #1,D0
- BLT.S gv_error -> bufferend reached ?
- CMP.B (A0)+,D1
- BEQ.S gv_skipspaces1
- ADDQ.L #1,D0 -> all spaces skipped; we have gone
- SUBQ.L #1,A0 -> one step too far.
-
- gv_skipname: -> skip programname (find next space)
- SUBQ.L #1,D0
- BLT.S gv_error -> bufferend reached ?
- CMP.B (A0)+,D1
- BNE.S gv_skipname
- ADDQ.L #1,D0 -> go one step back
- SUBQ.L #1,A0
-
- gv_skipspaces2: -> skip spaces before version-number
- SUBQ.L #1,D0
- BLT.S gv_error -> bufferend reached ?
- CMP.B (A0)+,D1
- BEQ.S gv_skipspaces2
- ADDQ.L #1,D0 -> go one step back
- SUBQ.L #1,A0
-
- MOVEQ #0,version
- MOVEQ #".",D1
- MOVEQ #0,D2
- gv_getversion1: -> get version until we found a "."
- SUBQ.L #1,D0
- BLT.S gv_error -> bufferend reached ?
- MOVE.B (A0)+,D2
- SUBI.B #"0",D2 -> Transform character "0".."9" into number
- MULU.W #10,version -> version:=version*10
- ADD.L D2,version -> +number
- CMP.B (A0),D1
- BNE.S gv_getversion1
- SUBQ.L #1,D0 -> skip "."
- ADDQ.L #1,A0
-
- MOVEQ #" ",D1
- MOVEQ #1,base
- gv_getversion2: -> get reversion
- SUBQ.L #1,D0
- BLT.S gv_error -> bufferend reached ?
- MOVE.B (A0)+,D2
- SUBI.B #"0",D2 -> Transform character "0".."9" into number
- MULU.W #10,version -> version:=version*10
- MULU.W #10,base -> base:=base*10
- ADD.L D2,version -> +number
- CMP.B (A0),D1
- BNE.S gv_getversion2
- BRA.S gv_return
-
- gv_error:
- MOVEQ #-1, version -> an error occured (bufferend reached)
-
- gv_return:
-
- ENDPROC version,base
- ->\\
-
-